﻿namespace ITSSP.Web.Models
{
	using System;
	using System.Collections;
	using System.Runtime.Serialization;
	using System.Security.Cryptography;
	using System.ServiceModel;
	using System.Text;

	public partial class User
	{
		#region Password hashing
		private static HashAlgorithm pHashAlgorithm = new SHA1Managed();

		public static byte[] generateNewPasswordHash(string password)
		{
			// generate salt
			byte[] saltBytes = new byte[4];
			RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
			rng.GetBytes(saltBytes);

			return generatePasswordHash(password, saltBytes);
		}

		public static byte[] generatePasswordHash(string password, byte[] salt)
		{
			// $hash = base64_decode($cryptedpassword);
			// # OpenLDAP uses a 4 byte salt, SunDS uses an 8 byte salt - both from char 20.
			// # so we also use a 4 byte salt
			// $salt = substr($hash,20);
			// $new_hash = base64_encode(mhash(MHASH_SHA1,$plainpassword.$salt).$salt);
			// if (strcmp($cryptedpassword,$new_hash) == 0)
			//     return true;
			// else
			//     return false;

			// decode unicode password
			Encoder enc = Encoding.UTF8.GetEncoder();
			byte[] unicodeText = new byte[password.Length * 2 + 4];
			int passwordByteCount = enc.GetBytes(password.ToCharArray(), 0, password.Length, unicodeText, 0, true);

			// copy salt after password
			salt.CopyTo(unicodeText, passwordByteCount);

			byte[] saltedHash = new byte[pHashAlgorithm.HashSize/8 + salt.Length];
			pHashAlgorithm.ComputeHash(unicodeText, 0, passwordByteCount + 4).CopyTo(saltedHash, 0);
			salt.CopyTo(saltedHash, pHashAlgorithm.HashSize/8);

			return saltedHash;
		}

		public bool authenticate(string password)
		{
			byte[] salt = new byte[4];
			Array.Copy(this.HashedPassword, 20, salt, 0, 4);
			byte[] passwordHashed = generatePasswordHash(password, salt);
			return System.Linq.Enumerable.SequenceEqual(this.HashedPassword, passwordHashed);
		}
		#endregion
	}

	public class AuthenticationFailedException : CommunicationException
	{
		public AuthenticationFailedException()
		{ }

		public AuthenticationFailedException(string msg)
			: base(msg)
		{ }
	}
}